﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Windows.Media.Imaging;

using System.Text;
using System.Threading;
using System.Collections;
using System.Runtime.InteropServices;

using Algorithms;

namespace GameEngine
{
    public class Character:AnimatedGameObject
    {
        /// <summary>
        ///     Current hitpoints of this character.
        /// </summary>
        protected int HitPoints { get; set; }
        /// <summary>
        ///     Animations for each of 8 directions- left, right, up, down in that order.
        /// </summary>
        protected Spritesheet[] _movementAnimations = new Spritesheet[8];
        /// <summary>
        ///     If true the movement animation is different for each direction, if false it's the same.
        /// </summary>
        protected bool _eightDirectionMovement = false;
        /// <summary>
        ///     Index to the movementAnimations array based on the direction of movement.
        /// </summary>
        protected int _directionIndex = 0;
        /// <summary>
        ///     Previous index to the movementAnimations array based on the direction of movement.
        /// </summary>
        protected int _previousDirectionIndex = 0;
        /// <summary>
        ///     Direction the character is facing.
        /// </summary>
        protected int _facing = 1;


        /// <summary>
        ///     Sets up 8 direction animations - up, right+up, right, right+down, down, left+down, left, left+up in that order.
        ///     0-th frame of each animation should be character standing still facing the desired direction.
        /// </summary>
        /// <param name="movementAnimations">Animations for the directions.</param>
        /// <param name="frameLength">
        /// How long(for how many seconds) the animation does not change frame.
        /// (e.g. 4 - frame changes after every 4 seconds).
        /// </param>
        public virtual void SetAnimation(Spritesheet[] movementAnimations, double frameLength)
        {
            //initializes the animations
            for (int i = 0; i < movementAnimations.Length; i++)
            {
                //this._movementAnimations[i] = new Spritesheet(movementAnimations[i]);
                //this._movementAnimations[i] = movementAnimations[i];
                this._movementAnimations[i] = Spritesheet.Clone(movementAnimations[i]);
            }
            this._animationSet = true;
            this._eightDirectionMovement = true;
            this._frameLength = frameLength;
        }


        public override void Animate(TimeSpan elapsedTime)
        {
            if (this._animationSet)
            {
                //uses base animation if the animation is the same for all directions
                if (this._eightDirectionMovement == false)
                    base.Animate(elapsedTime);
                //changes animation based on the direction of movement
                else
                {
                    //counts time
                    //this._elapsedSecondsAnimation += elapsedTime.TotalSeconds;

                    //counts distance travelled 
                    this._elapsedSecondsAnimation += Math.Sqrt(Math.Pow(this._positionChange.X,2) + Math.Pow(this._positionChange.Y,2));

                    this._previousDirectionIndex = this._directionIndex;
                    this._directionIndex = 0;
                    switch (this._horDir)
                    {
                        case HorizontalDirection.left:
                            switch (this._verDir)
                            {
                                case VerticalDirection.up:
                                    this._directionIndex += 8;
                                    break;
                                case VerticalDirection.down:
                                    this._directionIndex += 6;
                                    break;
                                case VerticalDirection.none:
                                    this._directionIndex += 7;
                                    break;
                            }
                            break;
                        case HorizontalDirection.right:
                            switch (this._verDir)
                            {
                                case VerticalDirection.up:
                                    this._directionIndex += 2;
                                    break;
                                case VerticalDirection.down:
                                    this._directionIndex += 4;
                                    break;
                                case VerticalDirection.none:
                                    this._directionIndex += 3;
                                    break;
                            }
                            break;
                        case HorizontalDirection.none:
                            switch (this._verDir)
                            {
                                case VerticalDirection.up:
                                    this._directionIndex += 1;
                                    break;
                                case VerticalDirection.down:
                                    this._directionIndex += 5;
                                    break;
                                case VerticalDirection.none:
                                    this._directionIndex += 0;
                                    break;
                            }
                            break;
                    }

                    //we update the facing direction only when the object moves
                    if (this._directionIndex != 0)
                        this._facing = this._directionIndex; 

                    //if the object is not moving sets it to the first frame of spritesheet 
                    if (_directionIndex == 0)
                        this._renderVisual = this._movementAnimations[this._facing-1].First();
                    else
                    {
                        //decreases the index so it starts at 0
                        this._directionIndex--;
                        //sets the visual from animation spritesheet
                        if (this._directionIndex == this._previousDirectionIndex)
                        {
                            //we only change frame if more than frameLength seconds has passed
                            if (this._elapsedSecondsAnimation >= this._frameLength)
                            {
                                this._renderVisual = this._movementAnimations[this._directionIndex].Next();
                                this._elapsedSecondsAnimation -= this._frameLength;
                            }
                        }
                        else
                        {
                            this._movementAnimations[this._previousDirectionIndex].Reset();
                            
                            this._renderVisual = this._movementAnimations[this._directionIndex].Next();

                            //we reset the counting
                            this._elapsedSecondsAnimation = 0;
                        }
                    }
                }
            }
        }


        /// <summary>
        ///     Creates new character with unique ID;
        /// </summary>
        public Character()
        {
            this._id = GetNextID();
        }


        /// <summary>
        ///     Moves the character with animation based on the direction.
        /// </summary>
        public override void Move(TimeSpan elapsedTime, IntSize tileSize)
        {
            //changes the animation

            //calls the base.Move to do other things
            base.Move(elapsedTime, tileSize);
        }
    }
}
